﻿#ifndef ___PCLIB_S_SHA1___
#define ___PCLIB_S_SHA1___


#include "../m_core/m_core.hpp"
#include "s_crypto_base.hpp"

#define  PCSHA1_LEN        (20)

/**
*@brief        SHA1计算
*/
class CPCSHA1 :CPCNoCopyable
{
public:
    explicit CPCSHA1(){ Init(); }

public:
    /**
    *@brief     直接获取字节或字符串的sha1
    *@param     src    [in]    输入的数据
    *@param     len    [in]    数据长度
    *@return    20字节的sha1
    */
    static std::string GetBytesSHA1(const void* src, size_t len)
    {
        CPCSHA1 sha1ctx;
        sha1ctx.Update(src, len);
        return sha1ctx.Finish();
    }

    /**
    *@brief     直接获取字节或字符串的sha1 hmac
    *@param     src[in]         输入的数据
    *@param     srclen[in]      数据长度
    *@param     keybytes[in]    密钥
    *@param     keylen[in]      密钥长度
    *@return    20字节的sha1 hmac
    */
    static std::string GetBytesSHA1HMAC (const void *src, size_t srclen,const void *keybytes, size_t keylen)
    {
        return PCGetBytesHMAC<CPCSHA1>(src,srclen,keybytes,keylen,64,PCSHA1_LEN);
    }

public:
    /**
    *@brief Initialize new context
    *
    */
    void Init()
    {
        /* SHA1 initialization constants */
        h[0] = 0x67452301;
        h[1] = 0xefcdab89;
        h[2] = 0x98badcfe;
        h[3] = 0x10325476;
        h[4] = 0xc3d2e1f0;
        count[0] = count[1] = 0;
    }

    /**
    * @brief Run your data through this
    *
    * @param p       Buffer to run SHA1 on
    * @param len     Number of bytes
    */
    void Update(const void *p, size_t len)
    {
        if( len == 0 || p == NULL)
            return;
        const uint8_t *data = (const uint8_t *)p;
        size_t i, j;

        j = (count[0] >> 3) & 63;
        if ((count[0] += (uint32_t)(len << 3)) < (len << 3))
        {
            count[1]++;
        }
        count[1] += (uint32_t)(len >> 29);
        if ((j + len) > 63)
        {
            memcpy(&buffer[j], data, (i = 64 - j));
            PCSHA1_transform(buffer);
            for (; i + 63 < len; i += 64)
            {
                PCSHA1_transform(data + i);
            }
            j = 0;
        }
        else
        {
            i = 0;
        }
        memcpy(&buffer[j], &data[i], len - i);
    }

    /**
    * @brief Add padding and return the message digest
    * @return Generated message digest
    */
    std::string Finish()
    {
        uint8_t digest[PCSHA1_LEN];
        uint32_t i;
        uint8_t finalcount[8];

        for (i = 0; i < 8; i++)
        {
            finalcount[i] = (uint8_t)((count[(i >= 4 ? 0 : 1)]
                >> ((3 - (i & 3)) * 8)) & 255);
        }
        Update((uint8_t *) "\200", 1);
        while ((count[0] & 504) != 448)
        {
            Update((uint8_t *) "\0", 1);
        }
        Update(finalcount, 8); /* Should cause SHA1_Transform */
        for (i = 0; i < PCSHA1_LEN; i++)
        {
            digest[i] = (uint8_t)
                ((h[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
        }

        /* Wipe variables */
        i = 0;
        memset(buffer, 0, 64);
        /* fast-pbkdf2 needs access to the state*/
        /*memset(context->h, 0, 20);*/
        memset(count, 0, 8);
        memset(finalcount, 0, 8);    /* SWR */
        return std::string((char*)digest, PCSHA1_LEN);
    }

private:
    /** Hash a single 512-bit block. This is the core of the algorithm.
    */
    void PCSHA1_transform(const uint8_t buffer[64])
    {
    //////////////////////////////////////////////////////////////////
    /* Generic C equivalent */
#define CPCSHA1_rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
#define CPCSHA1_ror(value, bits) (((value) << (32 - (bits))) | ((value) >> (bits)))

#define CPCSHA1_blk0le(i) (block[i] = (CPCSHA1_ror(block[i],8)&0xFF00FF00) \
                                 |(CPCSHA1_rol(block[i],8)&0x00FF00FF))
#define CPCSHA1_blk0be(i) block[i]
#define CPCSHA1_blk(i) (block[i&15] = CPCSHA1_rol(block[(i+13)&15]^block[(i+8)&15] \
                                 ^block[(i+2)&15]^block[i&15],1))

    /*
    * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
    *
    * Rl0() for little-endian and Rb0() for big-endian.  Endianness is
    * determined at run-time.
    */
#define CPCSHA1_Rl0(v,w,x,y,z,i) \
      z+=((w&(x^y))^y)+CPCSHA1_blk0le(i)+0x5A827999+CPCSHA1_rol(v,5);w=CPCSHA1_ror(w,2);
#define CPCSHA1_Rb0(v,w,x,y,z,i) \
      z+=((w&(x^y))^y)+CPCSHA1_blk0be(i)+0x5A827999+CPCSHA1_rol(v,5);w=CPCSHA1_ror(w,2);
#define CPCSHA1_R1(v,w,x,y,z,i) \
      z+=((w&(x^y))^y)+CPCSHA1_blk(i)+0x5A827999+CPCSHA1_rol(v,5);w=CPCSHA1_ror(w,2);
#define CPCSHA1_R2(v,w,x,y,z,i) \
      z+=(w^x^y)+CPCSHA1_blk(i)+0x6ED9EBA1+CPCSHA1_rol(v,5);w=CPCSHA1_ror(w,2);
#define CPCSHA1_R3(v,w,x,y,z,i) \
      z+=(((w|x)&y)|(w&x))+CPCSHA1_blk(i)+0x8F1BBCDC+CPCSHA1_rol(v,5);w=CPCSHA1_ror(w,2);
#define CPCSHA1_R4(v,w,x,y,z,i) \
      z+=(w^x^y)+CPCSHA1_blk(i)+0xCA62C1D6+CPCSHA1_rol(v,5);w=CPCSHA1_ror(w,2);
    //////////////////////////////////////////////////////////////////
        uint32_t a, b, c, d, e;
        static int one = 1;
        uint32_t block[16];
        memcpy(block, buffer, 64);

        /* Copy context->h[] to working vars */
        a = h[0];
        b = h[1];
        c = h[2];
        d = h[3];
        e = h[4];

        /* 4 rounds of 20 operations each. Loop unrolled. */
        if (1 == *(unsigned char*)&one) /* Check for endianess */
        {
            CPCSHA1_Rl0(a, b, c, d, e, 0);  CPCSHA1_Rl0(e, a, b, c, d, 1);  CPCSHA1_Rl0(d, e, a, b, c, 2);  CPCSHA1_Rl0(c, d, e, a, b, 3);
            CPCSHA1_Rl0(b, c, d, e, a, 4);  CPCSHA1_Rl0(a, b, c, d, e, 5);  CPCSHA1_Rl0(e, a, b, c, d, 6);  CPCSHA1_Rl0(d, e, a, b, c, 7);
            CPCSHA1_Rl0(c, d, e, a, b, 8);  CPCSHA1_Rl0(b, c, d, e, a, 9);  CPCSHA1_Rl0(a, b, c, d, e, 10); CPCSHA1_Rl0(e, a, b, c, d, 11);
            CPCSHA1_Rl0(d, e, a, b, c, 12); CPCSHA1_Rl0(c, d, e, a, b, 13); CPCSHA1_Rl0(b, c, d, e, a, 14); CPCSHA1_Rl0(a, b, c, d, e, 15);
        }
        else
        {
            CPCSHA1_Rb0(a, b, c, d, e, 0);  CPCSHA1_Rb0(e, a, b, c, d, 1);  CPCSHA1_Rb0(d, e, a, b, c, 2);  CPCSHA1_Rb0(c, d, e, a, b, 3);
            CPCSHA1_Rb0(b, c, d, e, a, 4);  CPCSHA1_Rb0(a, b, c, d, e, 5);  CPCSHA1_Rb0(e, a, b, c, d, 6);  CPCSHA1_Rb0(d, e, a, b, c, 7);
            CPCSHA1_Rb0(c, d, e, a, b, 8);  CPCSHA1_Rb0(b, c, d, e, a, 9);  CPCSHA1_Rb0(a, b, c, d, e, 10); CPCSHA1_Rb0(e, a, b, c, d, 11);
            CPCSHA1_Rb0(d, e, a, b, c, 12); CPCSHA1_Rb0(c, d, e, a, b, 13); CPCSHA1_Rb0(b, c, d, e, a, 14); CPCSHA1_Rb0(a, b, c, d, e, 15);
        }
        CPCSHA1_R1(e, a, b, c, d, 16); CPCSHA1_R1(d, e, a, b, c, 17); CPCSHA1_R1(c, d, e, a, b, 18); CPCSHA1_R1(b, c, d, e, a, 19);
        CPCSHA1_R2(a, b, c, d, e, 20); CPCSHA1_R2(e, a, b, c, d, 21); CPCSHA1_R2(d, e, a, b, c, 22); CPCSHA1_R2(c, d, e, a, b, 23);
        CPCSHA1_R2(b, c, d, e, a, 24); CPCSHA1_R2(a, b, c, d, e, 25); CPCSHA1_R2(e, a, b, c, d, 26); CPCSHA1_R2(d, e, a, b, c, 27);
        CPCSHA1_R2(c, d, e, a, b, 28); CPCSHA1_R2(b, c, d, e, a, 29); CPCSHA1_R2(a, b, c, d, e, 30); CPCSHA1_R2(e, a, b, c, d, 31);
        CPCSHA1_R2(d, e, a, b, c, 32); CPCSHA1_R2(c, d, e, a, b, 33); CPCSHA1_R2(b, c, d, e, a, 34); CPCSHA1_R2(a, b, c, d, e, 35);
        CPCSHA1_R2(e, a, b, c, d, 36); CPCSHA1_R2(d, e, a, b, c, 37); CPCSHA1_R2(c, d, e, a, b, 38); CPCSHA1_R2(b, c, d, e, a, 39);
        CPCSHA1_R3(a, b, c, d, e, 40); CPCSHA1_R3(e, a, b, c, d, 41); CPCSHA1_R3(d, e, a, b, c, 42); CPCSHA1_R3(c, d, e, a, b, 43);
        CPCSHA1_R3(b, c, d, e, a, 44); CPCSHA1_R3(a, b, c, d, e, 45); CPCSHA1_R3(e, a, b, c, d, 46); CPCSHA1_R3(d, e, a, b, c, 47);
        CPCSHA1_R3(c, d, e, a, b, 48); CPCSHA1_R3(b, c, d, e, a, 49); CPCSHA1_R3(a, b, c, d, e, 50); CPCSHA1_R3(e, a, b, c, d, 51);
        CPCSHA1_R3(d, e, a, b, c, 52); CPCSHA1_R3(c, d, e, a, b, 53); CPCSHA1_R3(b, c, d, e, a, 54); CPCSHA1_R3(a, b, c, d, e, 55);
        CPCSHA1_R3(e, a, b, c, d, 56); CPCSHA1_R3(d, e, a, b, c, 57); CPCSHA1_R3(c, d, e, a, b, 58); CPCSHA1_R3(b, c, d, e, a, 59);
        CPCSHA1_R4(a, b, c, d, e, 60); CPCSHA1_R4(e, a, b, c, d, 61); CPCSHA1_R4(d, e, a, b, c, 62); CPCSHA1_R4(c, d, e, a, b, 63);
        CPCSHA1_R4(b, c, d, e, a, 64); CPCSHA1_R4(a, b, c, d, e, 65); CPCSHA1_R4(e, a, b, c, d, 66); CPCSHA1_R4(d, e, a, b, c, 67);
        CPCSHA1_R4(c, d, e, a, b, 68); CPCSHA1_R4(b, c, d, e, a, 69); CPCSHA1_R4(a, b, c, d, e, 70); CPCSHA1_R4(e, a, b, c, d, 71);
        CPCSHA1_R4(d, e, a, b, c, 72); CPCSHA1_R4(c, d, e, a, b, 73); CPCSHA1_R4(b, c, d, e, a, 74); CPCSHA1_R4(a, b, c, d, e, 75);
        CPCSHA1_R4(e, a, b, c, d, 76); CPCSHA1_R4(d, e, a, b, c, 77); CPCSHA1_R4(c, d, e, a, b, 78); CPCSHA1_R4(b, c, d, e, a, 79);

        /* Add the working vars back into context.state[] */
        h[0] += a;
        h[1] += b;
        h[2] += c;
        h[3] += d;
        h[4] += e;

        /* Wipe variables */
        a = b = c = d = e = 0;
        memset(block, 0, 64);
    //////////////////////////////////////////////////////////////////
    #undef CPCSHA1_rol
    #undef CPCSHA1_ror
    #undef CPCSHA1_blk0le
    #undef CPCSHA1_blk0be
    #undef CPCSHA1_Rl0
    #undef CPCSHA1_Rb0
    #undef CPCSHA1_R1
    #undef CPCSHA1_R2
    #undef CPCSHA1_R3
    #undef CPCSHA1_R4
    //////////////////////////////////////////////////////////////////
    }

private:
    uint32_t h[5];
    /**< Context state */
    uint32_t count[2];
    /**< Counter       */
    uint8_t buffer[64];
    /**< SHA-1 buffer  */
};

#endif    //___PCLIB_S_SHA1___